﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System.Collections.Generic;

namespace block_explorer
{
    class Scene
    {
        private BlockExplorer mGame;
        private Map mMap;
        private Player mPlayer;

        private IList<IComponent> mEntities;
        
        private IList<SceneCamera> mCameras;
        private int mSelectedCamera;

        private IList<BaseSceneLight> mLights;

        public BlockExplorer Game { get { return mGame; } }
        public Map Map { get { return mMap; } }
        public Player Player { get { return mPlayer; } }

        public SceneCamera SelectedCamera { get { return mCameras[mSelectedCamera]; } }

        public Color AmbientLight { get; set; }
        public IList<BaseSceneLight> Lights { get { return mLights; } }

        public Scene(BlockExplorer game)
        {
            mGame = game;
            mEntities = new List<IComponent>();
            mLights = new List<BaseSceneLight>();
            mCameras = new List<SceneCamera>();
        }

        public void Initialize()
        {
            SectorComparer comparer = new SectorComparer(this);
            JobScheduler scheduler = new JobScheduler(comparer);

            //IMapBuilder builder = new NoiseBuilder();
            //IMapBuilder builder = new BasicHillsBuilder();
            //IMapBuilder builder = new AccidentalBuilder();
            IMapBuilder builder = new CaveBuilder();
            mMap = new Map(this, builder, scheduler);

            mPlayer = new Player();
            mPlayer.Scene = this;
            mPlayer.Position = new Vector3(-50.0f, 70.0f, 0.0f);
            mPlayer.Initialize(mGame.GraphicsDevice, mGame.Content);

            EntityDriver driver = new EntityDriver(mGame);
            driver.Target = mPlayer;

            mEntities.Add(scheduler);
            mEntities.Add(mMap);
            mEntities.Add(driver);
            mEntities.Add(mPlayer);
        }

        public void Destroy()
        {
            foreach (IComponent component in mEntities)
            {
                component.Destroy();
            }
        }


        public void LoadContent()
        {
            foreach (IComponent component in mEntities)
            {
                component.Initialize(mGame.GraphicsDevice, mGame.Content);
            }
        }

        public void Update(GameTime gameTime)
        {
            foreach (IComponent component in mEntities)
            {
                component.Update(gameTime);
            }

            foreach (SceneCamera camera in mCameras)
            {
                camera.Update(gameTime);
            }
        }

        public void Draw(Shaders.WorldShader effect)
        {
            Draw(effect, SelectedCamera);
        }

        public void Draw(Shaders.WorldShader effect, Camera camera)
        {
            effect.View = camera.View;
            effect.Projection = camera.Projection;

            foreach (var component in mEntities)
            {
                IDrawableComponent drawableComponent = component as IDrawableComponent;
                if (drawableComponent != null)
                {
                    drawableComponent.Draw(mGame.GraphicsDevice, camera, effect);
                }
            }
        }

        public void Draw2D(Shaders.Render2DEffect effect)
        {
            Draw2D(effect, SelectedCamera);
        }

        public void Draw2D(Shaders.Render2DEffect effect, Camera camera)
        {
            effect.View = camera.View;
            effect.Projection = camera.Projection;

            foreach (var component in mEntities)
            {
                IDrawableComponent drawableComponent = component as IDrawableComponent;
                if (drawableComponent != null)
                {
                    drawableComponent.Draw2D(mGame.GraphicsDevice, camera, effect);
                }
            }
        }

        public int AddLight(BaseSceneLight light)
        {
            mLights.Add(light);
            return mLights.Count - 1;
        }

        public int AddCamera(SceneCamera camera)
        {
            mCameras.Add(camera);
            return mCameras.Count - 1;
        }

        public void SelectCamera(int id)
        {
            mSelectedCamera = id;
        }

        public void ToggleNextCamera()
        {
            mSelectedCamera = (mSelectedCamera + 1) % mCameras.Count;
        }

        public SceneCamera GetCamera(int id)
        {
            return mCameras[id];
        }
    }
}
